/*
 * bitops.c
 *
 *  Created on: Mar 29, 2013
 *      Author: locpx
 */
#include "stdio.h"
#include "defines.h"
#include "extglobals.h"
#include "protos.h"

void displayBitBoard(BITBOARD bitboard)
{
	int square, rank, file;
	char boardc[90];

	for (square = 0; square < 90; square++)
	{
		if (isAllZeroBit(andTwoBitBoards(bitboard, BITBOARDSET[89 - square])))
			boardc[square] = '.';
		else
			boardc[square] = '1';
		printf("%c", boardc[square]);
	}

	printf("\n");
	printf("bb[0] = %u; bb[1] = %u; bb[2] = %u\n", bitboard.bb[0], bitboard.bb[1], bitboard.bb[2]);
	for (rank = 0; rank < 10; rank++)
	{
		for (file = 8; file >= 0; file--)
		{
			printf("%c", boardc[BOARDINDEX[file][rank]]);
		}
		printf("\n");
	}
	printf("\n");
}

/* setting all bits to 0 */
void clearBitBoard(BITBOARD* bitboard)
{
	bitboard->bb[0] = 0;
	bitboard->bb[1] = 0;
	bitboard->bb[2] = 0;
}

/* Logical operations like taking the AND of two bitboards */
BITBOARD andTwoBitBoards(BITBOARD bitboard1, BITBOARD bitboard2)
{
	BITBOARD result;

	result.bb[0] = bitboard1.bb[0] & bitboard2.bb[0];
	result.bb[1] = bitboard1.bb[1] & bitboard2.bb[1];
	result.bb[2] = bitboard1.bb[2] & bitboard2.bb[2];

	return result;
}

/* Logical operations like taking the AND of two bitboards */
BITBOARD orTwoBitBoards(BITBOARD bitboard1, BITBOARD bitboard2)
{
	BITBOARD result;

	result.bb[0] = bitboard1.bb[0] | bitboard2.bb[0];
	result.bb[1] = bitboard1.bb[1] | bitboard2.bb[1];
	result.bb[2] = bitboard1.bb[2] | bitboard2.bb[2];

	return result;
}

/* Logical operations like taking the AND of two bitboards */
BITBOARD xorTwoBitBoards(BITBOARD bitboard1, BITBOARD bitboard2)
{
	BITBOARD result;

	result.bb[0] = bitboard1.bb[0] ^ bitboard2.bb[0];
	result.bb[1] = bitboard1.bb[1] ^ bitboard2.bb[1];
	result.bb[2] = bitboard1.bb[2] ^ bitboard2.bb[2];

	return result;
}

/* Logical operations like taking the AND of numBB bitboards */
BITBOARD andMultipleBitBoards(BITBOARD bitboard[], int numBB)
{
	BITBOARD result = bitboard[0];

	int i;
	for (i = 1; i < numBB; i++)
	{
		result.bb[0] &= bitboard[i].bb[0];
		result.bb[1] &= bitboard[i].bb[1];
		result.bb[2] &= bitboard[i].bb[2];
	}

	return result;
}

/* Logical operations like taking the OR of numBB bitboards */
BITBOARD orMultipleBitBoards(BITBOARD bitboard[], int numBB)
{
	BITBOARD result = bitboard[0];

	int i;
	for (i = 1; i < numBB; i++)
	{
		result.bb[0] |= bitboard[i].bb[0];
		result.bb[1] |= bitboard[i].bb[1];
		result.bb[2] |= bitboard[i].bb[2];
	}

	return result;
}

/* Logical operations like taking the XOR of numBB bitboards */
BITBOARD xorMultipleBitBoards(BITBOARD bitboard[], int numBB)
{
	BITBOARD result = bitboard[0];

	int i;
	for (i = 1; i < numBB; i++)
	{
		result.bb[0] ^= bitboard[i].bb[0];
		result.bb[1] ^= bitboard[i].bb[1];
		result.bb[2] ^= bitboard[i].bb[2];
	}

	return result;
}

/* Logical operations like taking the NOT of a bitboard */
BITBOARD notBitBoard(BITBOARD bitboard)
{
	BITBOARD result = bitboard;

	result.bb[0] = ~bitboard.bb[0];
	result.bb[1] = ~bitboard.bb[1];
	result.bb[2] = ~bitboard.bb[2];

	result.bb[0] &= LAST_THIRTY_BIT_SET;
	result.bb[1] &= LAST_THIRTY_BIT_SET;
	result.bb[2] &= LAST_THIRTY_BIT_SET;

	return result;
}

/* Check if bitboard has only zero bits */
int isAllZeroBit(BITBOARD bitboard)
{
	return (bitboard.bb[0] == 0) && (bitboard.bb[1] == 0) && (bitboard.bb[2] == 0);
}

/*
 * Find the least significant index or position of the bits set to one in the U32 word
 * Note: this algorithm does not handle the zero input
 * See http://en.wikipedia.org/wiki/Find_first_set
 *
 * Don't use this if u32 = 0
 */
U32 findFirstOne(U32 u32)
{
	return INDEX32[((u32 & (-u32)) * DEBRUIJN32) >> 27];
}

/*
 * Find the most significant index or position of the bits set to one in the U32 word
 * Note: this algorithm does not handle the zero input
 * See http://en.wikipedia.org/wiki/Most_significant_bit
 *
 * Don't use this if u32 = 0
 */
U32 findLastOne(U32 u32)
{
	u32 |= u32 >> 1;
	u32 |= u32 >> 2;
	u32 |= u32 >> 4;
	u32 |= u32 >> 8;
	u32 |= u32 >> 16;
	u32 = (u32 >> 1) + 1;

	return INDEX32[(u32 * DEBRUIJN32) >> 27];
}

/**
 * Don't use this if bitboard = 0
 */
U32 findFirstOneInBitBoard(BITBOARD bitboard)
{
	if (bitboard.bb[0])
	{
		return findFirstOne(bitboard.bb[0]);
	}
	else if (bitboard.bb[1])
	{
		return 30 + findFirstOne(bitboard.bb[1]);
	}
	else if (bitboard.bb[2])
	{
		return 60 + findFirstOne(bitboard.bb[2]);
	}
	else
	{
		return 0;
	}
}

/**
 * Don't use this if bitboard = 0
 */
U32 findLastOneInBitBoard(BITBOARD bitboard)
{
	if (bitboard.bb[2])
	{
		return 60 + findLastOne(bitboard.bb[2]);
	}
	else if (bitboard.bb[1])
	{
		return 30 + findLastOne(bitboard.bb[1]);
	}
	else if (bitboard.bb[0])
	{
		return findLastOne(bitboard.bb[0]);
	}
	else
	{
		return 0;
	}
}

unsigned int bitCnt(U32 u32)
{
	// MIT HAKMEM algorithm, see http://graphics.stanford.edu/~seander/bithacks.html

	static const U32  M1 = 0x55555555;  // 1 zero,  1 one ...
	static const U32  M2 = 0x33333333;  // 2 zeros,  2 ones ...
	static const U32  M4 = 0x0F0F0F0F;  // 4 zeros,  4 ones ...
	static const U32  M8 = 0x00FF00FF;  // 8 zeros,  8 ones ...
	static const U32 M16 = 0x0000FFFF;  // 16 zeros, 16 ones ...

	u32 = (u32 & M1 ) + ((u32 >>  1) & M1 );   //put count of each  2 bits into those  2 bits
	u32 = (u32 & M2 ) + ((u32 >>  2) & M2 );   //put count of each  4 bits into those  4 bits
	u32 = (u32 & M4 ) + ((u32 >>  4) & M4 );   //put count of each  8 bits into those  8 bits
	u32 = (u32 & M8 ) + ((u32 >>  8) & M8 );   //put count of each 16 bits into those 16 bits
	u32 = (u32 & M16) + ((u32 >> 16) & M16);   //put count of each 32 bits into those 32 bits

	return (int)u32;
}

unsigned int bitCntBB(BITBOARD bitboard)
{
	return bitCnt(bitboard.bb[0]) + bitCnt(bitboard.bb[1]) + bitCnt(bitboard.bb[2]);
}
